package com.clementheliou.cheliou.dal.model.comment.builder;

import org.springframework.util.Assert;

import com.clementheliou.cheliou.dal.dao.camera.brand.CameraBrandDAO;
import com.clementheliou.cheliou.dal.dao.camera.model.CameraModelDAO;
import com.clementheliou.cheliou.dal.dao.city.CityDAO;
import com.clementheliou.cheliou.dal.dao.comment.CommentDAO;
import com.clementheliou.cheliou.dal.dao.country.CountryDAO;
import com.clementheliou.cheliou.dal.dao.member.MemberDAO;
import com.clementheliou.cheliou.dal.dao.photo.PhotoDAO;
import com.clementheliou.cheliou.dal.model.comment.Comment;
import com.clementheliou.cheliou.dal.model.member.Member;
import com.clementheliou.cheliou.dal.model.member.builder.MemberBuilder;
import com.clementheliou.cheliou.dal.model.photo.Photo;
import com.clementheliou.cheliou.dal.model.photo.builder.PhotoBuilder;
import com.clementheliou.cheliou.util.builder.ObjectBuilder;
import com.clementheliou.cheliou.util.time.TimeUtils;

/**
 * Builder used to get {@link Comment} instances for test purpose. It must only be used through one
 * of its static factory methods.
 * 
 * @author Clément HELIOU (clement.heliou@gmail.com)
 * @see {@link Comment};
 * @see {@link ObjectBuilder}.
 * @since 1.0
 */
public class CommentBuilder extends ObjectBuilder<Comment> {

	/**
	 * The {@link CameraBrandDAO} to be transmitted to the {@link PhotoBuilder} if the instance must
	 * be persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link PhotoBuilder} .
	 * @since 1.0
	 */
	private final CameraBrandDAO cameraBrandDAO;

	/**
	 * The {@link CameraModelDAO} to be transmitted to the {@link PhotoBuilder} if the instance must
	 * be persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link PhotoBuilder} .
	 * @since 1.0
	 */
	private final CameraModelDAO cameraModelDAO;

	/**
	 * The {@link CityDAO} to be transmitted to the {@link PhotoBuilder} if the instance must be
	 * persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link PhotoBuilder} .
	 * @since 1.0
	 */
	private final CityDAO cityDAO;

	/**
	 * The {@link CommentDAO} to be used to persist the built instance if needed.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	private final CommentDAO commentDAO;

	/**
	 * The {@link CountryDAO} to be transmitted to the {@link PhotoBuilder} if the instance must be
	 * persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link PhotoBuilder} .
	 * @since 1.0
	 */
	private final CountryDAO countryDAO;

	/**
	 * The {@link MemberDAO} to be transmitted to the {@link MemberBuilder} if the instance must be
	 * persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link MemberBuilder}.
	 * @since 1.0
	 */
	private final MemberDAO memberDAO;

	/**
	 * The {@link PhotoDAO} to be transmitted to the {@link PhotoBuilder} if the instance must be
	 * persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link PhotoBuilder} .
	 * @since 1.0
	 */
	private final PhotoDAO photoDAO;

	/**
	 * Private constructor from the DAO to be used.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param cameraBrandDAO the {@link CameraBrandDAO} to be used.
	 * @param cameraModelDAO the {@link CameraModelDAO} to be used.
	 * @param cityDAO the {@link CityDAO} to be used.
	 * @param commentDAO the {@link CommentDAO} to be used.
	 * @param countryDAO the {@link CommentDAO} to be used.
	 * @param memberDAO the {@link MemberDAO} to be used.
	 * @param photoDAO the {@link PhotoDAO} to be used.
	 * @since 1.0
	 */
	private CommentBuilder(CameraBrandDAO cameraBrandDAO, CameraModelDAO cameraModelDAO, CityDAO cityDAO, CommentDAO commentDAO, CountryDAO countryDAO, MemberDAO memberDAO, PhotoDAO photoDAO) {
		this.cameraBrandDAO = cameraBrandDAO;
		this.cameraModelDAO = cameraModelDAO;
		this.cityDAO = cityDAO;
		this.commentDAO = commentDAO;
		this.countryDAO = countryDAO;
		this.memberDAO = memberDAO;
		this.photoDAO = photoDAO;
	}

	/**
	 * Private default constructor.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	private CommentBuilder() {
		this(null, null, null, null, null, null, null);
	}

	/**
	 * Static factory method used to get a builder instance for a {@link Comment} object that must
	 * be persistent. It's mostly used on data access layer's methods tests.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param cameraBrandDAO the {@link CameraBrandDAO} to be used.
	 * @param cameraModelDAO the {@link CameraModelDAO} to be used.
	 * @param cityDAO the {@link CityDAO} to be used.
	 * @param commentDAO the {@link CommentDAO} to be used.
	 * @param countryDAO the {@link CommentDAO} to be used.
	 * @param memberDAO the {@link MemberDAO} to be used.
	 * @param photoDAO the {@link PhotoDAO} to be used.
	 * @return the builder instance to be used.
	 * @since 1.0
	 */
	public static CommentBuilder with(CameraBrandDAO cameraBrandDAO, CameraModelDAO cameraModelDAO, CityDAO cityDAO, CommentDAO commentDAO, CountryDAO countryDAO, MemberDAO memberDAO,
			PhotoDAO photoDAO) {
		return new CommentBuilder(cameraBrandDAO, cameraModelDAO, cityDAO, commentDAO, countryDAO, memberDAO, photoDAO);
	}

	/**
	 * Static factory method used to get a builder instance for a {@link Comment} object that must
	 * be persistent. It's mostly used on data access layer's methods tests.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param commentDAO the {@link CommentDAO} to be used.
	 * @param memberDAO the {@link MemberDAO} to be used.
	 * @return the builder instance to be used.
	 * @since 1.0
	 */
	public static CommentBuilder with(CommentDAO commentDAO, MemberDAO memberDAO) {
		return with(null, null, null, commentDAO, null, memberDAO, null);
	}

	/**
	 * Static factory method used to get a builder instance for a {@link Comment} object that must
	 * not be persistent. It's mostly used in tests not having a real data access.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the builder instance to be used.
	 * @since 1.0
	 */
	public static CommentBuilder withNoDependencies() {
		return new CommentBuilder();
	}

	/**
	 * Creates a default {@link Comment} mock that should never be persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the builder instance after this action.
	 * @since 1.0
	 */
	public CommentBuilder createDefaultCommentMock() {
		setInstance(createInstance(new Member(), new Photo()));
		return this;
	}

	/**
	 * Creates a default {@link Comment} instance, ready to be persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the builder instance after this action.
	 * @since 1.0
	 */
	public CommentBuilder createDefaultComment() {
		setInstance(createInstance());
		return this;
	}

	/**
	 * Creates a default {@link Comment} instance for the given {@link Photo}, ready to be
	 * persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param photoToComment the photo the created comment will apply to.
	 * @return the builder instance after this action.
	 * @since 1.0
	 */
	public CommentBuilder createDefaultCommentFor(final Photo photoToComment) {
		Assert.notNull(photoToComment, "A photo must be provided at this time");
		setInstance(createInstance(photoToComment));
		return this;
	}

	/**
	 * Persists the built {@link Comment} instance then flush the current session.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the builder instance after this action.
	 * @since 1.0
	 */
	public CommentBuilder persistAndFlush() {
		Assert.notNull(commentDAO, "commentDAO must not be null at this time");

		commentDAO.saveEntity(getObject());
		commentDAO.flushSession();

		return this;
	}

	/**
	 * Creates a {@link Comment} instance with default values, creating the photo it applies to.
	 * Created data have a functional sense.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the built instance.
	 * @since 1.0
	 */
	private Comment createInstance() {
		Assert.notNull(cameraBrandDAO, "cameraBrandDAO must not be null at this time");
		Assert.notNull(cameraModelDAO, "cameraModelDAO must not be null at this time");
		Assert.notNull(cityDAO, "cityDAO must not be null at this time");
		Assert.notNull(countryDAO, "countryDAO must not be null at this time");
		Assert.notNull(photoDAO, "photoDAO must not be null at this time");

		return createInstance(PhotoBuilder.with(cameraBrandDAO, cameraModelDAO, cityDAO, countryDAO, photoDAO).createDefaultPhoto().persistAndFlush().getObject());
	}

	/**
	 * Creates a {@link Comment} instance with default values for the given photo, creating the
	 * author giving it. Created data have a functional sense.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param photoToComment the photo the created default comment will apply to.
	 * @return the built instance.
	 * @since 1.0
	 */
	private Comment createInstance(final Photo photoToComment) {
		Assert.notNull(memberDAO, "memberDAO must not be null at this time");
		return createInstance(MemberBuilder.with(memberDAO).createDefaultMember().persistAndFlush().getObject(), photoToComment);
	}

	/**
	 * Creates a {@link Comment} instance with default values for the given photo and author.
	 * Created data have a functional sense.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param commentAuthor the member making the created default comment.
	 * @param photoToComment the photo the created default comment will apply to.
	 * @return the built instance.
	 * @since 1.0
	 */
	private Comment createInstance(final Member commentAuthor, final Photo photoToComment) {
		final Comment defaultComment = new Comment();

		defaultComment.setAdditionDate(TimeUtils.now());
		defaultComment.setAuthor(commentAuthor);
		defaultComment.setCommentedPhoto(photoToComment);
		defaultComment.setContent("content");

		return defaultComment;
	}
}
